package com.iob.coin.entity.questionTree;

import com.iob.coin.covid19kg.exceptions.QuestionTreeException;

import java.util.ArrayList;
import java.util.function.Function;

/**
 * @Author: GuoXinyuan
 * @Date: 2021/6/7
 * 代表一个三元组的树
 *
 */
public class TripleTree extends ResultTree {

    private final QuestionTree[] tripleArr = new QuestionTree[3]; //存储三元组的结构 主谓宾依次存放在0/1/2

    private final boolean isAskForSubject; //是否是对主语的提问(树被补完后三个节点可能都非空 需要在创建时记录)

    private boolean isCompleted = false; //是否已经被补充完成


    public TripleTree(QuestionTree s,QuestionTree p,QuestionTree o) throws QuestionTreeException {
        super(new ArrayList<>());

        isAskForSubject = s==null;

        tripleArr[0] = s;
        tripleArr[1] = p;
        tripleArr[2] = o;

        //当前问题树必须合法
        if(!this.isLegal()) throw new QuestionTreeException();
    }

    public QuestionTree getSubject(){
        return tripleArr[0];
    }

    public QuestionTree getProperty(){
        return tripleArr[1];
    }

    public QuestionTree getObject(){
        return tripleArr[2];
    }

//    public void setSubject(QuestionTree questionTree){
//        tripleArr[0] = questionTree;
//    }

//    public void setProperty(QuestionTree questionTree){
//        tripleArr[1] = questionTree;
//    }

//    public void setObject(QuestionTree questionTree){
//        tripleArr[2] = questionTree;
//    }
    /**
     * 判断当前问题树作为 root节点时是否合法 即除了根节点之外的节点不能有多个null
     * @return
     */
    private boolean isLegal(){
        //不能构建完全为空的树
        if(tripleArr[0]==null && tripleArr[1]==null && tripleArr[2]==null) return false;

        //不支持对谓语的提问
        if(tripleArr[0]!=null && tripleArr[1]==null && tripleArr[2]!=null) return false;

        for(QuestionTree t:tripleArr){
            if(t instanceof TripleTree){
                TripleTree son = (TripleTree) t;
                if(son.isAskForResource() || !son.isLegal()) return false;
            }
        }
        return true;
    }

    /**-------------------------------------------------------------------------------------------------
     * 以下在自然语言解析阶段使用
     */

    /**
     * 是否是针对主语的提问
     * @return
     */
    public boolean isAskForSubject(){
        return isAskForSubject;
    }

    /**
     * 是否是只针对一个resource
     * @return
     */
    public boolean isAskForResource(){
        return getProperty()==null; //当谓语为空时说明至少有两个null
    }

    /**
     * 获取提问目标子树 当有多个null时返回空
     * @return
     */
    public QuestionTree getTargetSubTree(){
        if(isAskForResource()) return null;
        if(isAskForSubject()) return getSubject();
        else return getObject();
    }

    /**
     * 获取查询来源子树
     * @return
     */
    public QuestionTree getArgumentSubTree(){
        if(isAskForSubject) return getObject();
        else return getSubject();
    }

    /**
     * 是否已经被补完
     * @return
     */
    public boolean isCompleted(){
        return isCompleted;
    }

    /**
     * 对树进行补完 将原来为空的节点补充为查询结果节点
     * @param resultTree
     */
    public void completeTree(ResultTree resultTree) throws QuestionTreeException {
        if(isAskForResource()) throw new QuestionTreeException();
        if(isAskForSubject) tripleArr[0] = resultTree;
        else tripleArr[2] = resultTree;
        setResultList(resultTree.getResultList());//当前节点的resultList即被补充进来的resultList
        isCompleted = true;
    }

    /**
     * 将标签子树替换为结果子树
     */
    public void replaceLabelTree(ResultTree resultTree, int flag) throws QuestionTreeException {
        if(!(tripleArr[flag] instanceof LabelTree)) throw new QuestionTreeException();
        tripleArr[flag] = resultTree;
    }

    /**
     * 获取内部主谓宾存放数组 方便某些情况遍历
     * @return
     */
    public QuestionTree[] getSPOArr(){
        QuestionTree[] temp = new QuestionTree[3];
        temp[0] = tripleArr[0];
        temp[1] = tripleArr[1];
        temp[2] = tripleArr[2];
        return temp;
    }

    /**
     * 获取树高
     * @return
     */
    @Override
    public int level() {
        Function<QuestionTree,Integer> calLevel = (node)-> node==null?0:node.level();
        int maxLevel = Math.max(
                Math.max(calLevel.apply(tripleArr[0]), calLevel.apply(tripleArr[1])),
                calLevel.apply(tripleArr[2])
        );
        return maxLevel + 1;
    }

}

